﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using Medianamik.Core.Logging;
using Medianamik.Core;

namespace Medianamik.Core.ImportExport
{
    public abstract class DataImporter<TType, TTypeProperty, TRecord, TRecordProperty>
    {
        #region Fields


        #endregion

        #region .ctors

        internal DataImporter(DataSource<TType, TTypeProperty, TRecord, TRecordProperty> dataSource)
        {
            _dataSource = dataSource;
        }

        #endregion

        #region Events

        public event EventHandler<RecordTypeImportingEventArgs<TType>> RecordTypeImporting;
        protected virtual ImportExportEventResult OnRecordTypeImporting(TType importedType, int importedTypeIndex,
            int importedTypesCount)
        {
            if (RecordTypeImporting != null)
            {
                RecordTypeImportingEventArgs<TType> e = new RecordTypeImportingEventArgs<TType>(importedType, importedTypeIndex, importedTypesCount);
                RecordTypeImporting(this, e);
                if (e.ImportExportEventResult.Cancel)
                {
                    OnRecordTypesImported(importedTypeIndex, importedTypesCount, true);
                    return e.ImportExportEventResult;
                }
            }

            return new ImportExportEventResult();
        }

        public event EventHandler<RecordTypePropertyImportingEventArgs<TTypeProperty>> RecordTypePropertyImporting;
        protected virtual ImportExportEventResult OnRecordTypePropertyImporting(TTypeProperty importedProperty, int importedPropertyIndex,
            int importedPropertiesCount)
        {
            if (RecordTypePropertyImporting != null)
            {
                RecordTypePropertyImportingEventArgs<TTypeProperty> e = new RecordTypePropertyImportingEventArgs<TTypeProperty>(importedProperty, importedPropertyIndex, importedPropertiesCount);
                RecordTypePropertyImporting(this, e);
                if (e.ImportExportEventResult.Cancel)
                {
                    OnRecordTypesImported(importedPropertyIndex, importedPropertiesCount, true);
                    return e.ImportExportEventResult;
                }
            }

            return new ImportExportEventResult();
        }

        public event EventHandler<RecordTypesImportedEventArgs> RecordTypesImported;
        protected virtual void OnRecordTypesImported(int iterations,
            int importedTypesCount, bool canceled)
        {
            if (RecordTypesImported != null)
            {
                RecordTypesImportedEventArgs e = new RecordTypesImportedEventArgs(iterations, importedTypesCount, canceled);
                RecordTypesImported(this, e);
            }
        }

        public event EventHandler<RecordTypesImportingEventArgs<TType>> RecordTypesImporting;
        protected virtual bool OnRecordTypesImporting(IList<TType> importedTypes)
        {
            if (RecordTypesImporting != null)
            {
                RecordTypesImportingEventArgs<TType> e = new
                    RecordTypesImportingEventArgs<TType>(importedTypes);
                RecordTypesImporting(this, e);
                return e.Cancel;
            }

            return false;
        }

        public event EventHandler<RecordTypePropertiesImportingEventArgs<TType, TTypeProperty>> RecordTypePropertiesImporting;
        protected virtual bool OnRecordTypePropertiesImporting(TType importedType, IList<TTypeProperty> importedTypeProperties)
        {
            if (RecordTypePropertiesImporting != null)
            {
                RecordTypePropertiesImportingEventArgs<TType, TTypeProperty> e = new
                    RecordTypePropertiesImportingEventArgs<TType, TTypeProperty>(importedType, importedTypeProperties);
                RecordTypePropertiesImporting(this, e);
                return e.Cancel;
            }

            return false;
        }

        public event EventHandler<RecordTypePropertyImportedEventArgs> RecordTypePropertyImported;
        protected virtual void OnRecordTypePropertyImported(NodeTypeProperty recordTypeProperty)
        {
            if (RecordTypePropertyImported != null)
            {
                RecordTypePropertyImportedEventArgs e = new
                    RecordTypePropertyImportedEventArgs(recordTypeProperty);
                RecordTypePropertyImported(this, e);
            }
        }

        public event EventHandler<RecordTypesImportFailedEventArgs> RecordTypesImportFailed;
        protected virtual void OnRecordTypesImportFailed(string message)
        {
            if (RecordTypesImportFailed != null)
            {
                RecordTypesImportFailedEventArgs e = new RecordTypesImportFailedEventArgs(message);
                RecordTypesImportFailed(this, e);
            }
        }

        public event EventHandler<RecordTypeImportFailedEventArgs<TType>> RecordTypeImportFailed;
        protected virtual bool OnRecordTypeImportFailed(TType importedType, int importedTypeIndex,
            int importedTypesCount, string message)
        {
            if (RecordTypeImportFailed != null)
            {
                RecordTypeImportFailedEventArgs<TType> e = new RecordTypeImportFailedEventArgs<TType>(importedType, importedTypeIndex, importedTypesCount, message);
                RecordTypeImportFailed(this, e);
                if (e.Cancel)
                {
                    OnRecordTypesImported(importedTypeIndex, importedTypesCount, true);
                    return e.Cancel;
                }
            }

            return false;
        }

        public event EventHandler<DuplicateRecordTypeImportingEventArgs<TType>> DuplicateRecordTypeImporting;
        protected virtual ImportExportEventResult OnDuplicateRecordTypeImporting(string importedTypeName, TType importedType, int importedTypeIndex,
            int importedTypesCount)
        {
            if (DuplicateRecordTypeImporting != null)
            {
                DuplicateRecordTypeImportingEventArgs<TType> e = new DuplicateRecordTypeImportingEventArgs<TType>(importedTypeName,
                    importedType, importedTypeIndex, importedTypesCount);
                DuplicateRecordTypeImporting(this, e);
                if (e.ImportExportEventResult.Cancel)
                {
                    OnRecordTypesImported(importedTypeIndex, importedTypesCount, true);
                    return e.ImportExportEventResult;
                }
            }

            return new ImportExportEventResult();
        }

        public event EventHandler<RecordTypePropertyImportFailedEventArgs<TTypeProperty>> RecordTypePropertyImportFailed;
        protected virtual bool OnRecordTypePropertyImportFailed(TTypeProperty importedProperty, int importedPropertyIndex,
            int importedPropertiesCount, string message)
        {
            if (RecordTypeImportFailed != null)
            {
                RecordTypePropertyImportFailedEventArgs<TTypeProperty> e =
                    new RecordTypePropertyImportFailedEventArgs<TTypeProperty>(importedProperty,
                        importedPropertyIndex, importedPropertiesCount, message);
                RecordTypePropertyImportFailed(this, e);
                if (e.Cancel)
                {
                    return e.Cancel;
                }
            }

            return false;
        }

        public event EventHandler<InvalidRecordTypeIdEventArgs<TType>> InvalidRecordTypeId;
        protected virtual ImportExportEventResult OnInvalidRecordTypeId(Guid? importedTypeId, TType importedType, int importedTypeIndex,
            int importedTypesCount)
        {
            if (InvalidRecordTypeId != null)
            {
                InvalidRecordTypeIdEventArgs<TType> e = new InvalidRecordTypeIdEventArgs<TType>(importedTypeId,
                    importedType, importedTypeIndex, importedTypesCount);
                InvalidRecordTypeId(this, e);
                if (e.ImportExportEventResult.Cancel)
                {
                    OnRecordTypesImported(importedTypeIndex, importedTypesCount, true);
                    return e.ImportExportEventResult;
                }
            }

            return new ImportExportEventResult();
        }

        #endregion

        #region Properties

        public IEnumerable<NodeType> RecordTypes
        {
            get { return _recordTypes.AsEnumerable(); }
        }
        List<NodeType> _recordTypes = new List<NodeType>();

        protected static ILogger Logger
        {
            get
            {
                return _logger
                    ?? (_logger = new ObjectFactory<ILogger>().Get("ImportExportLogger"));
            }
        }
        private static ILogger _logger;

        protected DataSource<TType, TTypeProperty, TRecord, TRecordProperty> DataSource
        {
            get { return _dataSource; }
        }
        DataSource<TType, TTypeProperty, TRecord, TRecordProperty> _dataSource;

        #endregion

        #region Methods

        public void ImportRecordTypes()
        {
            List<TType> importedTypes = new List<TType>(DataSource.GetRecordTypes());
            bool canceled = false;
            bool skip = false;
            int iterations = 0;
            ImportExportEventResult result = new ImportExportEventResult();

            canceled = OnRecordTypesImporting(importedTypes);

            while ((!canceled) && (iterations < importedTypes.Count))
            {
                TType importedType = importedTypes[iterations];

                result = OnRecordTypeImporting(importedType, iterations, importedTypes.Count);
                canceled = result.Cancel;
                skip = result.Skip;

                if (!canceled && !skip)
                {
                    canceled = ImportRecordType(importedType, iterations, importedTypes.Count);
                }

                iterations++;
            }

            OnRecordTypesImported(iterations, importedTypes.Count,
                canceled);
        }

        private bool ImportRecordType(TType importedType, int importedTypeIndex,
            int importedTypesCount)
        {
            //TODO : Ajouter validations supplémentaires (exemple : doublons)
            string importedTypeName = GetImportedTypeName(importedType);

            if (String.IsNullOrEmpty(importedTypeName))
                return OnRecordTypeImportFailed(importedType, importedTypeIndex, importedTypesCount, "Record type name cannot be null or empty.");

            ImportExportEventResult result = new ImportExportEventResult();

            if (RecordTypes.Any(rt => rt.Name.Equals(importedTypeName, StringComparison.OrdinalIgnoreCase)))
            {
                result = OnDuplicateRecordTypeImporting(importedTypeName, importedType, importedTypeIndex, importedTypesCount);
            }

            if (result.Cancel)
            {
                return true;
            }
            else if (!result.Skip)
            {
                Guid? importedTypeId = GetImportedTypeId(importedType);

                if (!importedTypeId.HasValue || importedTypeId.Value == Guid.Empty)
                {
                    importedTypeId = GetNewRecordTypeId();

                    if (!importedTypeId.HasValue || importedTypeId.Value == Guid.Empty)
                    {
                        result = OnInvalidRecordTypeId(importedTypeId, importedType, importedTypeIndex, importedTypesCount);

                        if (result.Cancel)
                        {
                            return true;
                        }
                        else if (!result.Skip)
                        {

                        }
                    }
                }
            }
        }

        private void ImportRecordTypeProperty(TType importedType, TTypeProperty importedProperty)
        {
            //TODO : Ajouter validations supplémentaires (exemple : doublons)
            //string importedTypePropertyName = GetImportedTypePropertyName(importedProperty);

            //if (String.IsNullOrEmpty(importedTypePropertyName))
            //{
            //    if (OnRecordTypePropertyImportFailed(importedProperty, j,
            //        importedProperties.Count, "Record type property name cannot be null or empty."))
            //        break;
            //}
            //else
            //{
            //    DataType sqlDataType = GetImportedTypePropertySQLDataType(importedProperty);

            //    if (sqlDataType == null)
            //    {
            //        if (OnRecordTypePropertyImportFailed(importedProperty, j,
            //        importedProperties.Count, "SQL Data Type of Record type property cannot be null."))
            //            break;
            //    }
            //    else
            //    {
            //        NodeTypeProperty recordTypeProperty = new NodeTypeProperty(recordType, importedTypePropertyName,
            //        sqlDataType);

            //        recordProperties.Add(recordTypeProperty);

            //        OnRecordTypePropertyImported(recordTypeProperty);
            //    }
            //}

            throw new NotImplementedException();
        }

        

        #endregion
    }

    #region EventArgs

    public abstract class ImportExportWithResultEventArgs : EventArgs
    {
        public ImportExportEventResult ImportExportEventResult
        {
            get { return _importExportEventResult; }
        }
        ImportExportEventResult _importExportEventResult = new ImportExportEventResult();
    }

    public class RecordTypeImportingEventArgs<T> : ImportExportWithResultEventArgs
    {
        public RecordTypeImportingEventArgs(T importedType,
            int importedTypeIndex, int importedTypesCount)
        {
            _importedType = importedType;
            _importedTypeIndex = importedTypeIndex;
            _importedTypesCount = importedTypesCount;
        }

        #region Properties

        public T ImportedType
        {
            get { return _importedType; }
        }
        T _importedType;

        public int ImportedItemIndex
        {
            get { return _importedTypeIndex; }
        }
        int _importedTypeIndex;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        #endregion
    }

    public class RecordTypeImportFailedEventArgs<T> : CancelEventArgs
    {
        public RecordTypeImportFailedEventArgs(T importedType,
            int importedTypeIndex, int importedTypesCount, string message)
        {
            _importedType = importedType;
            _importedTypeIndex = importedTypeIndex;
            _importedTypesCount = importedTypesCount;
            _message = message;
        }

        #region Properties

        public T ImportedType
        {
            get { return _importedType; }
        }
        T _importedType;

        public int ImportedItemIndex
        {
            get { return _importedTypeIndex; }
        }
        int _importedTypeIndex;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        public string Message
        {
            get { return _message; }
        }
        string _message;

        #endregion
    }

    public class RecordTypePropertyImportingEventArgs<T> : ImportExportWithResultEventArgs
    {
        public RecordTypePropertyImportingEventArgs(T importedProperty,
            int importedPropertyIndex, int importedPropertiesCount)
        {
            _importedProperty = importedProperty;
            _importedPropertyIndex = importedPropertyIndex;
            _importedPropertiesCount = importedPropertiesCount;
        }

        #region Properties

        public T ImportedProperty
        {
            get { return _importedProperty; }
        }
        T _importedProperty;

        public int ImportedPropertyIndex
        {
            get { return _importedPropertyIndex; }
        }
        int _importedPropertyIndex;

        public int ImportedPropertiesCount
        {
            get { return _importedPropertiesCount; }
        }
        int _importedPropertiesCount;

        #endregion
    }

    public class RecordTypePropertyImportFailedEventArgs<T> : CancelEventArgs
    {
        public RecordTypePropertyImportFailedEventArgs(T importedProperty,
            int importedPropertyIndex, int importedPropertiesCount, string message)
        {
            _importedProperty = importedProperty;
            _importedPropertyIndex = importedPropertyIndex;
            _importedPropertiesCount = importedPropertiesCount;
            _message = message;
        }

        #region Properties

        public T ImportedProperty
        {
            get { return _importedProperty; }
        }
        T _importedProperty;

        public int ImportedPropertyIndex
        {
            get { return _importedPropertyIndex; }
        }
        int _importedPropertyIndex;

        public int ImportedPropertiesCount
        {
            get { return _importedPropertiesCount; }
        }
        int _importedPropertiesCount;

        public string Message
        {
            get { return _message; }
        }
        string _message;

        #endregion
    }

    public class RecordTypesImportingEventArgs<T> : CancelEventArgs
    {
        public RecordTypesImportingEventArgs(IList<T> importedTypes)
        {
            _importedTypes = importedTypes;
        }

        #region Properties

        public IList<T> ImportedTypes
        {
            get { return _importedTypes; }
        }
        IList<T> _importedTypes;

        #endregion
    }

    public class RecordTypePropertiesImportingEventArgs<TType, TTypeProperty> : CancelEventArgs
    {
        public RecordTypePropertiesImportingEventArgs(TType importedType, IList<TTypeProperty> importedTypeProperties)
        {
            _importedTypeProperties = importedTypeProperties;
            _importedType = importedType;
        }

        #region Properties

        public IList<TTypeProperty> ImportedTypeProperties
        {
            get { return _importedTypeProperties; }
        }
        IList<TTypeProperty> _importedTypeProperties;

        public TType ImportedType
        {
            get { return _importedType; }
        }
        TType _importedType;

        #endregion
    }

    public class RecordTypesImportedEventArgs : EventArgs
    {
        public RecordTypesImportedEventArgs(int iterations,
            int importedTypesCount, bool canceled)
        {
            _canceled = canceled;
            _iterations = iterations;
            _importedTypesCount = importedTypesCount;
        }

        #region Properties

        public bool Canceled
        {
            get { return _canceled; }
        }
        bool _canceled;

        public int Iterations
        {
            get { return _iterations; }
        }
        int _iterations;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        #endregion
    }

    public class RecordTypePropertyImportedEventArgs : EventArgs
    {
        public RecordTypePropertyImportedEventArgs(NodeTypeProperty recordTypeProperty)
        {
            _recordTypeProperty = recordTypeProperty;
        }

        #region Properties

        public NodeTypeProperty NodeTypeProperty
        {
            get { return _recordTypeProperty; }
        }
        NodeTypeProperty _recordTypeProperty;

        #endregion
    }

    public class RecordTypesImportFailedEventArgs : EventArgs
    {
        public RecordTypesImportFailedEventArgs(string message)
        {
            _message = message;
        }

        public string Message
        {
            get { return _message; }
        }
        string _message;
    }

    public class DuplicateRecordTypeImportingEventArgs<T> : ImportExportWithResultEventArgs
    {
        public DuplicateRecordTypeImportingEventArgs(string importedTypeName, T importedType,
            int importedTypeIndex, int importedTypesCount)
        {
            _importedTypeName = importedTypeName;
            _importedType = importedType;
            _importedTypeIndex = importedTypeIndex;
            _importedTypesCount = importedTypesCount;
        }

        #region Properties

        public T ImportedType
        {
            get { return _importedType; }
        }
        T _importedType;

        public int ImportedItemIndex
        {
            get { return _importedTypeIndex; }
        }
        int _importedTypeIndex;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        public string ImportedTypeName
        {
            get { return _importedTypeName; }
        }
        string _importedTypeName;

        #endregion
    }

    public class InvalidRecordTypeIdEventArgs<T> : ImportExportWithResultEventArgs
    {
        public InvalidRecordTypeIdEventArgs(Guid? importedTypeId, T importedType,
            int importedTypeIndex, int importedTypesCount)
        {
            _importedTypeId = importedTypeId;
            _importedType = importedType;
            _importedTypeIndex = importedTypeIndex;
            _importedTypesCount = importedTypesCount;
        }

        #region Properties

        public T ImportedType
        {
            get { return _importedType; }
        }
        T _importedType;

        public int ImportedItemIndex
        {
            get { return _importedTypeIndex; }
        }
        int _importedTypeIndex;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        public Guid? ImportedTypeId
        {
            get { return _importedTypeId; }
        }
        Guid? _importedTypeId;

        #endregion
    }

    #endregion
}
